package com.xcity.game.employee;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;

import com.xcity.db.entity.Employee;
import com.xcity.db.entity.Player;
import com.xcity.db.mapper.EmployeeMapper;
import com.xcity.game.combat.attr.CombatAttributeType;
import com.xcity.game.common.ICalculator;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.skill.SkillTypes;
import com.xcity.game.worldevent.WorldEvent;
import com.xcity.game.worldevent.WorldEventService;
import com.xcity.pomelo.push.SyncPush;
import com.xcity.pomelo.push.sync.CustomSync;
import com.xcity.pomelo.push.sync.LongSync;
import com.xcity.pomelo.response.EmployeeInfoResponse.EmployeeInfo;
import com.xcity.util.DBUtils;

import gnu.trove.list.TIntList;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import gnu.trove.procedure.TIntProcedure;
import gnu.trove.procedure.TObjectProcedure;
import naga.x.App;

public class EmployeeManager implements ICalculator, EmployeeListener {

	protected Player owner;
	
	protected TLongObjectMap<Employee> employees; // key: the employee's unique id (mask:1)
	protected TIntObjectMap<List<Employee>> groupByTemplates; // 按模板分组(key: templateId) (mask:2)
	protected List<Employee> sortedList; // 有序列表（只在玩家登录时排一次序） (mask:4)
	
	protected static final Logger LOG = EmployeeService.LOG;
	
	public EmployeeManager(Player owner, List<Employee> list) {
		this.owner = owner;
		this.employees = new TLongObjectHashMap<Employee>();
		this.groupByTemplates = new TIntObjectHashMap<List<Employee>>();
		this.sortedList = list != null ? list : new ArrayList<>();
		for (int i = 0, size = this.sortedList.size(); i < size; i++) {
			Employee e = list.get(i);
			e.setVm(this);
			this.employees.put(e.getId(), e);
			List<Employee> group = this.groupByTemplates.get(e.getTemplate().getId());
			if (group == null) {
				group = new LinkedList<Employee>();
				this.groupByTemplates.put(e.getTemplate().getId(), group);
			}
			group.add(e);
		}
	}

	public Player getOwner() {
		return owner;
	}

	public void setOwner(Player owner) {
		this.owner = owner;
	}

	public synchronized void addEmployee(Employee employee, boolean sync, String cause) {
		DBUtils.save(employee, EmployeeMapper.class); // TODO
		add(employee);
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_EMPLOYEE_ADDED, getOwner(), employee, cause);
		if (sync) {
			getOwner().send(new CustomSync(SyncPush.SYNC_EMPLOYEE, new EmployeeInfo(employee)));
		}
		fireEmployeeAdded(employee);
		LOG.info("[EMPLOYEE ADD]ID[{}]EMPLOYEE[{}]EID[{}]CAUSE[{}]", getOwner().getId(), employee.getTemplate().getId(), employee.getId(), cause);
	}
	
	private synchronized void add(Employee employee) {
		employees.put(employee.getId(), employee);
		List<Employee> group = groupByTemplates.get(employee.getTemplate().getId());
		if (group == null) {
			group = new LinkedList<Employee>();
			groupByTemplates.put(employee.getTemplate().getId(), group);
		}
		group.add(employee);
		sortedList.add(employee);
	}

	public synchronized Collection<Employee> getEmployees() {
		return employees.valueCollection();
	}
	
	public List<Employee> getSortedList() {
		return sortedList;
	}
	
	public synchronized Employee get(long id) {
		return employees.get(id);
	}
	
	public synchronized Employee remove(long id) throws EmployeeException {
		Employee employee = employees.get(id);
		if (employee == null) {
			throw new EmployeeException(MessageConst.EMPLOYEE_NOT_EXIST);
		}
		if (employee.hasEquipment()) {
			throw new EmployeeException(MessageConst.EMPLOYEE_FIRE_HAS_EQUIP);
		}
		remove(employee, 0x7); // mask full: 0b111
		fireEmployeeRemoved(employee);
		return employee;
	}
	
	private synchronized void remove(Employee employee, int mask) {
		if ((mask & 1) != 0) {
			employees.remove(employee.getId());
		}
		if ((mask & 2) != 0) {
			List<Employee> group = groupByTemplates.get(employee.getTemplate().getId());
			group.remove(employee);
		}
		if ((mask & 4) != 0) {
			sortedList.remove(employee);
		}
	} 
	
	@Override
	public void fireEmployeeAdded(Employee employee) {
		owner.getFormations().fireEmployeeAdded(employee);
		fireCombosChanged(employee);
	}

	@Override
	public void fireEmployeeRemoved(Employee e) {
		DBUtils.delete(e.getId(), EmployeeMapper.class);
//		owner.getBuildings().fireEmployeeRemoved(e);
		owner.getBuildings2().fireEmployeeRemoved(e);
		owner.getConquests().fireEmployeeRemoved(e);
		owner.getFormations().fireEmployeeRemoved(e);
		owner.getAssists().fireEmployeeRemoved(e);
		owner.send(new LongSync(SyncPush.SYNC_EMPLOYEE_REMOVED, e.getId()));
		fireCombosChanged(e);
	}
	
	public synchronized int size() {
		return employees.size();
	}
	
	public synchronized void save() {
		employees.forEachValue(new TObjectProcedure<Employee>() {
			@Override
			public boolean execute(Employee employee) {
				save(employee);
				return true;
			}
		});
	}
	
	private void save(Employee employee) {
		try {
			DBUtils.update(employee, EmployeeMapper.class);
		} catch (Exception e) {
			LOG.error("[EMPLOYEE SAVE ERROR]ID[" + owner.getId()
						+ "]EMPLOYEE[" + employee.getTemplate().getId()
						+ "]EID[" + employee.getId()
						+ "]EXP[" + employee.getExp()
						+ "]LVL[" + employee.getLevel()
						+ "]", e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T calc(int id, Object... vals) {
		switch (id) {
			case SkillTypes.REDUCE_BAD_WORLD_EVENT_LOSSES:
				return (T) calc9_12((long[]) vals[0], id);
			case SkillTypes.ADD_GOOLD_WORLD_EVENT_INCOME:
				return (T) calc9_12((long[]) vals[0], id);
			case SkillTypes.ADD_BUILD_CURE:
			case SkillTypes.ADD_BUILD_RESERVE:
			case SkillTypes.ADD_BUILD_INCOME:
				return (T) sumSkillVal((long[]) vals[0], id);
			case SkillTypes.ADD_BUILD_INCOME_ONLY_ONE_EMPLOYEE:
				return (T) calc20((long[]) vals[0]);
			case SkillTypes.ADD_ALL_EMPLOYEE_GLOBAL_ABILITY:
				return (T) calc18();
		}
		return null;
	}
	
	// 世界事件影响
	private Object calc9_12(long[] employeeIds, int skillType) {
		WorldEventService service = App.getApp().getServiceManager().get(WorldEventService.class);
		WorldEvent evt = service != null ? service.getCurrentEvent() : null;
		if (evt == null
				|| (skillType == SkillTypes.REDUCE_BAD_WORLD_EVENT_LOSSES && evt.isGood())
				|| (skillType == SkillTypes.ADD_GOOLD_WORLD_EVENT_INCOME && !evt.isGood())
				) {
			return 0f;
		}
		return sumSkillVal(employeeIds, skillType);
	}
	
	private Float sumSkillVal(long[] employeeIds, int skillType) {
		if (employeeIds == null) {
			return 0f;
		}
		float val = 0f;
		for (long id : employeeIds) {
			Employee e;
			if (id != 0 && (e = get(id)) != null) {
//				EmployeeSkill skill = e.getSkillByGroup(skillType);
//				if (skill != null) {
//					val += skill.getValue(0);
//				}
			}
		}
		return val;
	}
	
	private Float calc20(long[] employeeIds) {
		if (employeeIds != null && employeeIds.length == 1) {
			long id = employeeIds[0];
			Employee e;
			if (id != 0 && (e = get(id)) != null) {
//				EmployeeSkill skill = e.getSkillByGroup(SkillTypes.ADD_BUILD_INCOME_ONLY_ONE_EMPLOYEE);
//				if (skill != null) {
//					return skill.getValue(0);
//				}
			}
		}
		return 0f;
	}
	
	private Float calc18() {
		float val = 0;
//		for (int i = 0; i < employees.size(); i++) {
//			Employee e = employees.get(i);
//			EmployeeSkill skill;
//			if (e.getState() == State.DEFENSE
//					&& (skill = e.getSkillByGroup(SkillTypes.ADD_ALL_EMPLOYEE_GLOBAL_ABILITY)) != null) {
//				val += skill.getValue(0);
//			}
//		}
		return val;
	}
	
//	public void recoverAttr(String cause) {
//		owner.poolSet(PlayerPool.PROPERTY_LAST_RECOVER_EMPLOYEE_ATTR_DAY, TimeUpdater.getInstance().today());
//		for (int i = 0, size = employees.size(); i < size; i++) {
//			Employee e = employees.get(i);
//			int val = e.getMaxEnergy() - e.getEnergy();
//			e.addEnergy(val, cause);
//			val = e.getMaxHP() - e.getHP();
//			e.addHP(val, cause);
//		}
//	}
//	
//	public List<Employee> filter(State state) {
//		List<Employee> employees = this.employees;
//		List<Employee> list = new ArrayList<Employee>(employees.size());
//		for (int i = 0; i < employees.size(); i++) {
//			Employee e = employees.get(i);
//			if (e.getState() == state) {
//				list.add(e);
//			}
//		}
//		return list;
//	}
//	
//	public int size(State... states) {
//		List<Employee> employees = this.employees;
//		int c = 0;
//		for (int i = 0; i < employees.size(); i++) {
//			Employee e = employees.get(i);
//			if (ArraysUtils.contains(e.getState(), states)) {
//				c++;
//			}
//		}
//		return c;
//	}
	
	public int size(int templateId, EmployeeFilter filter) {
		List<Employee> group = groupByTemplates.get(templateId);
		if (group == null) {
			return 0;
		}
		if (filter == null) {
			return group.size();
		}
		int c = 0;
		for (int i = group.size() - 1; i >= 0; i--) {
			Employee e = group.get(i);
			if (filter.doFilter(e)) {
				++c;
			}
		}
		return c;
	}
	
	/**
	 * 批量删除员工（卡牌）
	 * @param except 忽略的卡牌
	 * @param arrays -> arrays[2n]: 卡牌id, arrays[2n+1]: 删除数量
	 * @return
	 */
	public synchronized boolean batchRemove(final Employee except, final int[] arrays, final String cause) {
		EmployeeFilter filter = new EmployeeFilter() {
			@Override
			public boolean doFilter(Employee employee) {
				return employee.getId() != except.getId() && !employee.isTrained();
			}
		};
		// 先检测卡牌是否充足，再批量删除
		for (int i = 0; i < arrays.length; i += 2) {
			int id = arrays[i];
			int count = arrays[i + 1];
			if (id != 0 && count > 0) {
				int size = size(id, filter);
				if (size < count) {
					return false;
				}
			}
		}
		// 删卡
		for (int i = 0; i < arrays.length; i += 2) {
			int id = arrays[i];
			int count = arrays[i + 1];
			if (id == 0 || count <= 0) {
				continue;
			}
			List<Employee> group = groupByTemplates.get(id);
			Iterator<Employee> itr = group.iterator();
			while (itr.hasNext()) {
				Employee employee = itr.next();
				if (filter.doFilter(employee)) {
					if (employee.hasEquipment()) {
						employee.unEquips(cause);
					}
					itr.remove();
					remove(employee, 0x5); // mask: 0b101
					fireEmployeeRemoved(employee);
					LOG.info("[EMPLOYEE REMOVE]ID[{}]EMPLOYEE[{}={}]CAUSE[{}]", getOwner().getId(), employee.getId(), employee.getTemplate().getId(), cause);
					if (--count <= 0) {
						break;
					}
				}
			}
		}
		return true;
	}
	
//	/**
//	 * 检测是否有另外一个同类型的已经培养过的员工
//	 * @param target
//	 * @return
//	 */
//	public synchronized boolean hasTheSameKindTrainedEmployee(Employee target) {
//		if (target.isTrained()) {
//			return false;
//		}
//		List<Employee> group = groupByTemplates.get(target.getTemplate().getId());
//		if (group == null) { // !!impossible
//			return false;
//		}
//		for (Employee employee : group) {
//			if (employee.getId() != target.getId() && employee.isTrained()) {
//				return true;
//			}
//		}
//		return false;
//	}
	
	private void fireCombosChanged(Employee src) {
		TIntList relateds = EmployeeUtils.getComboRelated(src.getTemplate().getId());
		if (relateds != null) {
			relateds.forEach(new TIntProcedure() {
				@Override
				public boolean execute(int templateId) {
					List<Employee> list = groupByTemplates.get(templateId);
					if (list != null) {
						for (Employee e : list) {
							e.setCombos(null);
							e.fireAttributesChanged(CombatAttributeType.MASK_FULL);
						}
					}
					return true;
				}
			});
		}
	}
	
}
